package org.lex.app;

import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;

public abstract class Application {
	private static Application instance = null;
	private ApplicationContext context = null; // lazy initialized
	private EventListenerList exitListenerList = null;

	protected Application() {
		super();
		this.exitListenerList = new EventListenerList();
	}

	public synchronized static <T extends Application> T getApplication(Class<T> c) {
		if (null == instance) {
			try {
				instance = c.newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return c.cast(instance);
	}

	public static <T extends Application> void launch(final Class<T> c, final String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				final Application app = Application.getApplication(c);
				app.initialize(args);
				this.configMainWindow(app);
			}

			private void configMainWindow(final Application app) {
				Window window = app.getRootWindow();
				if (window instanceof JFrame) {
					((JFrame) window).setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
				} else if (window instanceof JDialog) {
					((JDialog) window).setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
				}
				window.addWindowListener(new WindowAdapter() {
					@Override
					public void windowClosing(WindowEvent e) {
						app.exit();
					}
				});
			}
		});
	}

	public ApplicationContext getContext() {
		if (null == this.context) {
			this.context = new ApplicationContext(this);
		}
		return this.context;
	}

	public void exit() {
		if (false == this.canExit())
			return;
		else
			this.willExit();
		this.shutdown();
		try {
			this.getContext().getResourceManager().save();
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.getRootWindow().dispose();
		System.exit(0);
	}

	public void addExitListener(ExitListener l) {
		this.exitListenerList.add(ExitListener.class, l);
	}

	public void removeExitListener(ExitListener l) {
		this.exitListenerList.remove(ExitListener.class, l);
	}

	public ExitListener[] getExitListeners() {
		return this.exitListenerList.getListeners(ExitListener.class);
	}

	public abstract Window getRootWindow();

	public abstract String getApplicationID();

	protected abstract void initialize(String[] args);

	protected void shutdown() {
	}

	private boolean canExit() {
		// Guaranteed to return a non-null array
		Object[] listeners = exitListenerList.getListenerList();
		// Process the listeners last to first, notifying
		// those that are interested in this event
		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == ExitListener.class) {
				if (false == ((ExitListener) listeners[i + 1]).canExit())
					return false;
			}
		}
		return true;
	}

	// exit is confirmed
	private void willExit() {
		Object[] listeners = exitListenerList.getListenerList();
		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == ExitListener.class)
				((ExitListener) listeners[i + 1]).willExit();
		}
	}
}
